package site.teamo.biu.flink.web.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.internal.engine.ValidatorImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import site.teamo.biu.flink.web.exception.BiuResponseCode;
import site.teamo.biu.flink.web.exception.BiuValidationException;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 爱做梦的锤子
 * @create 2020/11/13
 */
@Component
@Aspect
@Slf4j
public class ValidationAspect {

    @Value("${biu-flink.aspect.validation.print:false}")
    private boolean print;

    /**
     * 创建一个校验工具
     */
    private static final ValidatorImpl validator;

    static {
        try (ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory()) {
            validator = (ValidatorImpl) factory.getValidator();
        }

    }

    @Around("@annotation(site.teamo.biu.flink.web.annotation.Validation)")
    public Object validation(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        /*
          先校验方法入参
         */
        Set<ConstraintViolation<Object>> constraintViolations = validator.validateParameters(pjp.getTarget(), method, args);
        if (constraintViolations.size() > 0) {
            validationResult(constraintViolations, pjp);
        }
        //再循环遍历所有参数，进行参数校验
        for (Object parameter : args) {
            //对参数进行校验
            constraintViolations = validator.validate(parameter);
            //封装错误信息
            if (constraintViolations.size() > 0) {
                validationResult(constraintViolations, pjp);
            }
        }
        // 目标方法执行
        return pjp.proceed();
    }

    private void validationResult(Set<ConstraintViolation<Object>> constraintViolations, ProceedingJoinPoint pjp) {
        Map<String, String> result = constraintViolations.stream()
                .collect(Collectors.toMap(cv -> cv.getPropertyPath().toString(), ConstraintViolation::getMessage));
        //启动配置打印校验结果，则进行校验结果打印
        if (print) {
            result.forEach((key, value) -> log.error("validation[{}.{}]=>{}:{}",
                    pjp.getTarget().getClass().getName(),
                    pjp.getSignature().getName(),
                    key,
                    value
            ));
        }
        throw new BiuValidationException(BiuResponseCode.PARAMETER_ERROR.code, result);
    }
}
